
#ifndef SX_AUTO_REFERENCE_ARRAY_LIST_H
#define SX_AUTO_REFERENCE_ARRAY_LIST_H

#include <sxArrayList.h>

namespace Sx {
namespace Util {

/* The AutoReferenceArrayList stores a list of objects and only allows 
 * sequential access of elements by reference. This means that elements cannot
 * be added to this list. The only way to add to this list is to call the
 * getNext() function. The reference returned by the getNext function will
 * always be the last element of the array. When this function is called the
 * internal reference count will increase; therefore when the getNext()
 * function is called again it will reference a higher index in the internal
 * array list. This class is designed for high-performance algorithms that
 * need continuously allocated memory, but cannot afford to have constant
 * object copies.
 */
template <typename T>
class AutoReferenceArrayList {
public:
	AutoReferenceArrayList();
	AutoReferenceArrayList(const AutoReferenceArrayList& list);
	AutoReferenceArrayList(unsigned int initialAllocationSize);
	virtual ~AutoReferenceArrayList();

	T& getNext();
	bool get(unsigned int index, T& element);
	void reset();
	void clear();

	unsigned int size() const;
	bool invalidateGreaterThan(unsigned int index);

	AutoReferenceArrayList& operator = (const AutoReferenceArrayList& a);

protected:
	static const unsigned int DEFAULT_ALLOCATION_SIZE = 100;

	ArrayList<T> data;
	unsigned int defaultAllocationSize;
	unsigned int freeIndex;
};

template <typename T>
AutoReferenceArrayList<T>::AutoReferenceArrayList() {
	this->defaultAllocationSize = AutoReferenceArrayList::DEFAULT_ALLOCATION_SIZE;
	this->data = ArrayList<T>(this->defaultAllocationSize);
	this->freeIndex = 0;
}

template <typename T>
AutoReferenceArrayList<T>::AutoReferenceArrayList(const AutoReferenceArrayList& list) {
	this->data = ArrayList<T>(list.data);
	this->freeIndex = list.freeIndex;
	this->defaultAllocationSize = list.defaultAllocationSize;
}

template <typename T>
AutoReferenceArrayList<T>::AutoReferenceArrayList(unsigned int initialAllocationSize) {
	this->defaultAllocationSize = initialAllocationSize;
	this->data = ArrayList<T>(this->defaultAllocationSize);
	this->freeIndex = 0;
}

template <typename T>
AutoReferenceArrayList<T>::~AutoReferenceArrayList() {
	
}

template <typename T>
T& AutoReferenceArrayList<T>::getNext() {
	if ( this->freeIndex == this->data.capacity() )
		this->data.resize(this->data.size() * 2);
	T& next = this->data[this->freeIndex];
	this->freeIndex++;
	return next;
}

template <typename T>
bool AutoReferenceArrayList<T>::get(unsigned int index, T& element) {
	if ( index >= this->freeIndex ) return false;
	element = this->data[index];
	return true;
}

template <typename T>
void AutoReferenceArrayList<T>::reset() {
	this->data.clear();
	this->data = ArrayList<T>(this->defaultAllocationSize);
	this->freeIndex = 0;
}

template <typename T>
void AutoReferenceArrayList<T>::clear() {
	this->reset();
}

template <typename T>
unsigned int AutoReferenceArrayList<T>::size() const {
	return this->freeIndex;
}

template <typename T>
bool AutoReferenceArrayList<T>::invalidateGreaterThan(unsigned int index) {
	if ( index >= this->freeIndex ) return false;
	if ( index + 1 >= data.size() ) this->data.resize(this->data.size() * 2);
	this->freeIndex = index + 1;
	return true;
}

template <typename T>
AutoReferenceArrayList<T>& AutoReferenceArrayList<T>::operator = (const AutoReferenceArrayList& a) {
	if ( this == &a ) return *this;
	this->data = ArrayList<T>(a.data);
	this->defaultAllocationSize = a.defaultAllocationSize;
	this->freeIndex = a.freeIndex;
	return *this;
}

}

}

#endif
